use std::fmt::Debug;
use crate::arena::Arena;

#[derive(Debug, Clone)]
struct Node<T> {
    value: T,
    next: Option<usize>
}

impl<T> Node<T> {
    fn new(value: T) -> Self {
        Self {
            value,
            next: None,
        }
    }
}

pub struct LinkedList<T> {
    head: Option<usize>,
    arena: Arena<Node<T>>
}

impl<T> LinkedList<T> {
    pub fn new() -> Self {
        Self {
            head: None,
            arena: Arena::new(),
        }
    }

    pub fn push_back(&mut self, value: T) {
        let new_node_ptr = self.arena.alloc(Node::new(value));
        if let Some(head_ptr) = self.head {
            let new_node = self.arena.deref_mut(new_node_ptr);
            new_node.next = Some(head_ptr);
            self.head = Some(new_node_ptr)
        } else {
            self.head = Some(new_node_ptr)
        }
    }

    pub fn pop_back(&mut self) {
        if let Some(head_ptr) = self.head {
            let head_node = self.arena.deref(head_ptr);
            self.head = head_node.next;
            self.arena.dealloc(head_ptr);
        }
    }

}


impl<T:Debug + std::fmt::Display> LinkedList<T> {
    pub fn debug_print(&self) {
        print!("[");
        let mut iter = self.head;
        while let Some(ptr) = iter {
            let node = self.arena.deref(ptr);
            print!("{} ", node.value);
            iter = node.next;
        }
        print!("]\n");
    }
}

#[cfg(test)]
mod test {
    use crate::linked_list::LinkedList;

    #[test]
    fn test_linked_list() {
        let mut li = LinkedList::new();
        li.debug_print();
        for i in 0..10 {
            li.push_back(i);
        }
        li.debug_print();
        for _ in 0..5 {
            li.pop_back()
        }
        li.debug_print();
    }

}
